<template>
  <div class="publish" @contextmenu.prevent>
    <studio-header />
    <div class="content-box">
      <div class="container" :style="containerStyle">
        <grid-layout
          ref="gridLayout"
          :layout="gridList"
          :col-num="dataStore.gridColNum"
          :row-height="dataStore.gridRowHeight"
          :is-draggable="false"
          :is-resizable="false"
          :is-mirrored="false"
          :vertical-compact="false"
          :margin="dataStore.girdMargin"
          :use-css-transforms="true"
        >
          <template v-for="(item, index) in gridList">
            <grid-item
              v-if="item.chartType === 'filter'"
              :id="`preview-grid-item-${item.i}`"
              :key="item.i"
              class="grid-item filter"
              :scale="dataStore.scaleValue"
              :x="grids[index].x"
              :y="grids[index].y"
              :w="grids[index].w"
              :h="grids[index].h"
              :i="grids[index].i"
              :min-w="2"
              :min-h="1"
            >
              <div
                :ref="`control-list-${item.i}`"
                class="control-box-list"
                :class="`list-${item.layout}`"
              >
                <template v-for="(controlConfig, j) in item.fieldConfig">
                  <controlVue
                    :key="j"
                    v-model="controlConfig.value"
                    :grid-item="item"
                    :field-config="controlConfig"
                    :layout="item.layout"
                    :control-type="controlConfig.controlRender"
                    :prop="{ size: 'default' }"
                    :label="controlConfig.name"
                    :options="controlConfig.options"
                    @change="itemChange(item, controlConfig)"
                  ></controlVue>
                </template>
              </div>
            </grid-item>
            <grid-item
              v-else-if="item.chartType === 'table'"
              :id="`preview-grid-item-${item.i}`"
              :ref="`grid-item-${item.i}`"
              :key="`preview-${item.i}`"
              class="grid-item"
              :class="{
                'hover-border':
                  currentFilterFormItemAsCharts.length === 0 &&
                  checkIfShowHoverStatus(item.widgetId),
                'select-border':
                  currentFilterFormItemAsCharts.length === 0 &&
                  selectedParentList.includes(item.widgetId),
                active:
                  currentFilterFormItemAsCharts.includes(item.widgetId) ||
                  filterAssociationChart.includes(item.widgetId),
              }"
              :x="grids[index].x"
              :y="grids[index].y"
              :w="grids[index].w"
              :h="grids[index].h"
              :i="grids[index].i"
            >
              <div v-if="item.isLoading" class="loading-mask">
                <div v-if="item.isLoading" class="loading-status">
                  <a-icon class="sync-icon" type="sync" spin /><span
                    class="loading-text"
                    >加载中...</span
                  >
                </div>
                <!-- <img class="loading-status-img" src="~@/assets/chart-loading.png" alt="加载中..."> -->
              </div>
              <div
                v-if="hideWidgetList.includes(item.widgetId)"
                :class="{
                  'interact-mask': true,
                  'no-title':
                    !item.chartOptions.title || !item.chartOptions.showChart,
                }"
              ></div>
              <div
                v-if="
                  !item.isLoading &&
                  item.chartOptions &&
                  item.chartOptions.value &&
                  item.chartOptions.value.length === 0
                "
                class="no-data"
              >
                <div class="no-data-block">
                  <img
                    class="no-data-img"
                    src="~@/assets/no-data.png"
                    alt="no-data"
                  />
                  <span class="no-data-text">{{
                    errorFilterWidgets.includes(item.i)
                      ? errorMsgMap[item.i]
                      : '暂无数据'
                  }}</span>
                </div>
              </div>
              <div
                v-if="item.chartOptions"
                class="title-box"
                :class="{
                  'no-title': item.chartOptions.title === '',
                }"
              >
                {{ item.chartOptions ? item.chartOptions.title || '' : '' }}
              </div>
              <pageTableVue
                :id="`grid-${item.i}`"
                :ref="`chart-${item.i}`"
                :is-sortable="true"
                :config="item"
                :class="{
                  'no-title': item.chartOptions.title === '',
                }"
                @pagechange="
                  (pageInfo) => {
                    onPageChange(item, index, pageInfo)
                  }
                "
                @sortchange="
                  (sortData) => {
                    sortchange(item, index, sortData)
                  }
                "
              >
              </pageTableVue>
            </grid-item>
            <grid-item
              v-else-if="item.chartType === 'configuration'"
              :id="`preview-grid-item-${item.i}`"
              :ref="`grid-item-${item.i}`"
              :key="`preview-${item.i}`"
              class="grid-item"
              :x="item.x"
              :y="item.y"
              :w="item.w"
              :h="item.h"
              :i="item.i"
            >
              <a-tooltip
                v-if="item.chartType === 'configuration'"
                placement="top"
                :title="
                  configurationExecuteId !== item.widgetId ? '执行' : '停止'
                "
              >
                <a-icon-font
                  v-if="configurationExecuteId !== item.widgetId"
                  type="iconkaishi"
                  :class="{
                    'grid-item-execute': true,
                    disable: configurationExecuteId,
                  }"
                  @click.stop="saveConfigurationParams(item)"
                />
                <a-icon-font
                  v-else
                  type="icontingzhi"
                  class="grid-item-execute"
                  @click.stop="stopConfigurationExecute(item)"
                />
              </a-tooltip>
              <div
                v-if="item.chartOptions"
                class="title-box"
                :class="{
                  'no-title': item.chartOptions.title === '',
                }"
              >
                {{ item.chartOptions ? item.chartOptions.title || '' : '' }}
              </div>

              <operator-configuration
                :style="{
                  height:
                    item.chartOptions && item.chartOptions.title
                      ? 'calc(100% - 38px)'
                      : '100%',
                }"
                :config="item.chartOptions"
              />
              <div
                v-if="configurationExecuteId === item.widgetId"
                class="progress-bar"
              ></div>
            </grid-item>
            <grid-item
              v-else-if="item.chartType === 'text'"
              :id="`preview-grid-item-${item.i}`"
              :ref="`grid-item-${item.i}`"
              :key="`preview-${item.i}`"
              class="grid-item"
              :class="{
                'remove-border': !item.chartOptions.showBorder,
              }"
              :x="grids[index].x"
              :y="grids[index].y"
              :w="grids[index].w"
              :h="grids[index].h"
              :i="grids[index].i"
            >
              <div v-if="item.isLoading" class="loading-mask">
                <div v-if="item.isLoading" class="loading-status">
                  <a-icon class="sync-icon" type="sync" spin /><span
                    class="loading-text"
                    >加载中...</span
                  >
                </div>
              </div>
              <div
                v-if="item.chartOptions"
                class="title-box"
                :class="{
                  'remove-title': !item.chartOptions.showTitle,
                }"
              >
                {{ item.chartOptions ? item.chartOptions.title || '' : '' }}
              </div>
              <text-editor
                :id="`preview-grid-item-${item.i}`"
                class="text-editor"
                :class="{
                  'height-reset': !item.chartOptions.showTitle,
                }"
                :text-value="item.chartOptions.value"
                :read-only="true"
              >
              </text-editor>
            </grid-item>
            <grid-item
              v-else-if="checkIsFilterForm(item)"
              :id="`filter-form-grid-item-${item.i}`"
              :ref="`grid-item-${item.i}`"
              :key="`filter-form-${item.i}`"
              class="grid-item"
              :class="{
                'active-border': currentFilterIndex === item.i,
              }"
              :x="item.x"
              :y="item.y"
              :w="item.w"
              :h="item.h"
              :i="item.i"
            >
              <div
                v-if="item.chartOptions"
                class="title-box"
                :class="{
                  'no-title': item.chartOptions.title === '',
                }"
              >
                {{ item.chartOptions ? item.chartOptions.title || '' : '' }}

                <filter-form-toolbar
                  v-if="checkFilterFormIsEnabled(item) && showFilterForm()"
                  :grid-item="item"
                  :index="index"
                  :disabled="isFilterFormTaskRunning"
                  :is-running="checkIsRunning(item)"
                  @start="
                    (_) => {
                      filterFormClicked(item)
                      filterFormExecute(true, item, index)
                    }
                  "
                  @stop="
                    (_) => {
                      filterFormClicked(item)
                      filterFormExecute(false, item, index)
                    }
                  "
                  @reset="
                    () => {
                      filterFormClicked(item)
                      filterFormReset(item)
                    }
                  "
                />
              </div>
              <div
                class="filter-form-hander"
                @click="filterFormClicked(item)"
                @mouseenter="
                  () => {
                    handleFilterFormMouseEnter(item)
                  }
                "
                @mouseleave="
                  () => {
                    handleFilterFormMouseLeave(item)
                  }
                "
              >
                <filter-form
                  v-if="showFilterForm()"
                  :ref="`filter-form-${item.i}`"
                  :dataset="dataset"
                  :pipeline-id="pipelineId"
                  :node-id-map="relationship || {}"
                  :config="item"
                  @change="
                    (dataSet) => {
                      filterChange(dataSet, item, index)
                    }
                  "
                ></filter-form>
              </div>
            </grid-item>
            <grid-item
              v-else
              :id="`preview-grid-item-${item.i}`"
              :ref="`grid-item-${item.i}`"
              :key="`preview-${item.i}`"
              class="grid-item"
              :class="{
                'hover-border':
                  currentFilterFormItemAsCharts.length === 0 &&
                  checkIfShowHoverStatus(item.widgetId),
                'select-border':
                  currentFilterFormItemAsCharts.length === 0 &&
                  selectedParentList.includes(item.widgetId),
                active:
                  currentFilterFormItemAsCharts.includes(item.widgetId) ||
                  filterAssociationChart.includes(item.widgetId),
              }"
              :x="grids[index].x"
              :y="grids[index].y"
              :w="grids[index].w"
              :h="grids[index].h"
              :i="grids[index].i"
            >
              <div v-if="item.isLoading" class="loading-mask">
                <div v-if="item.isLoading" class="loading-status">
                  <a-icon class="sync-icon" type="sync" spin /><span
                    class="loading-text"
                    >加载中...</span
                  >
                </div>
              </div>
              <!-- 标题 -->
              <div
                v-if="
                  !item.showChart &&
                  item.chartOptions &&
                  item.chartType !== 'filter'
                "
                class="title-box"
                :class="{
                  'no-title': [undefined, null, ''].includes(
                    item.chartOptions.title
                  ),
                }"
              >
                {{ item.chartOptions ? item.chartOptions.title || '' : '' }}
              </div>
              <div
                v-if="hideWidgetList.includes(item.widgetId)"
                :class="{
                  'interact-mask': true,
                  'no-title':
                    !item.chartOptions.title || !item.chartOptions.showChart,
                }"
              ></div>
              <div
                v-if="
                  !item.isLoading &&
                  item.chartOptions &&
                  item.chartOptions.value &&
                  checkIsNoData(item)
                "
                class="no-data"
              >
                <div class="no-data-block">
                  <img
                    class="no-data-img"
                    src="~@/assets/no-data.png"
                    alt="no-data"
                  />
                  <span class="no-data-text">{{
                    errorFilterWidgets.includes(item.i)
                      ? errorMsgMap[item.i]
                      : '暂无数据'
                  }}</span>
                </div>
              </div>
              <template>
                <Chart
                  v-show="
                    checkIsGeoMap(item) ||
                    (item.showChart &&
                      item.chartOptions.value &&
                      !checkIsNoData(item))
                  "
                  :id="`preview-grid-${item.i}`"
                  :config="resize ? getChartOptions(item) : item.chartOptions"
                  :chart-type="item.chartType"
                  @chartinit="
                    (instance) => {
                      onChartInstanceInit(instance, item)
                    }
                  "
                  @chartevent="
                    (data) => {
                      onChartEvent(data, item, index)
                    }
                  "
                >
                  <div
                    v-if="setting.tooltipShow"
                    slot="graph_tooltip"
                    slot-scope="{ setting, chart }"
                    class="graph-tooltip"
                    :style="{
                      left: setting.nodeLinkTooltipPosition.left + 'px',
                      top: setting.nodeLinkTooltipPosition.top + 'px',
                    }"
                  >
                    <p
                      v-for="(attrInfo, i) in getGraphTooltipInfo(
                        chart,
                        setting
                      )"
                      :key="i"
                    >
                      {{ attrInfo }}
                    </p>
                  </div>
                </Chart>
              </template>
            </grid-item>
          </template>
        </grid-layout>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import { Component, Vue, Watch } from 'vue-property-decorator'
import StudioHeader from '@/components/studio/StudioHeader.vue'
import VisualizationStore from '@/store/modules/visualization'
import ProjectStore from '@/store/modules/project'
import { getWidgetData, widgetQueryByProject } from '@/api/widget'
import {
  ColumnInterface,
  componentTypeEnum,
  ControlConfigurationInterface,
  controlTypeEnum,
  GridItemDataInterface,
  gridItemFilterInterface,
  KeyValueStore,
} from '@/components/vueGridLayout/interfate/grid-Item-data-inteface'
import Chart from '@/components/chart/chart.vue'
import controlVue from '@/components/control/control.vue'
import TextEditor from '@/components/textEditor/textEditor.vue'
import GridLayout from '@/components/vueGridLayout/components/GridLayout.vue'
import pageTableVue from '@/components/pageTable/pageTable.vue'
import GridItem from '@/components/vueGridLayout/components/GridItem.vue'
import OperatorConfiguration from '@/components/visualization/OperatorConfiguration.vue'
import {
  bindChartData,
  buildSaveParameters,
  checkFilterFormIsEnabled,
  checkIsFilterForm,
  filterGraphData,
  getFilterDataFromFilterForm,
  mergeFilterFormFilters,
} from '@/util/common'
import {
  checkArrayIncludes,
  setWatermark,
  deleteWatermark,
  removeResizeListener,
} from '@/util/util'
import { simpleUpdate } from '@/api/task'
import axios from '@/api/http'

import { pipelineExecute, pipelineStop } from '@/api/pipeline'
import { isArray } from '@/util/data-view-table-helper'
import { getDefaultValueFromDashBoardData } from '@/components/visualization/controller-setting'
import { enableConvertToCoor } from '@/util/recommend-visualization'
import { isString, cloneDeep, debounce } from 'lodash'
import { GEOMAP_NAME, GRAPH_ARRAY } from '@/config/contant'
import {
  parseChartOptionToFunction,
  parseGetDataResult,
  getWidgetGraphData,
} from '@/components/common/VisualizationDashboard/util'
// import { VISUALIZATION_PATH } from '@/router/studio'

import FilterFormToolbar from '@/components/filter-form/filter-form-toolbar.vue'
import FilterForm from '@/components/filter-form/index.vue'
import { HoverFilterFormInfo } from '@/components/visualization/util'
import { queryDatalist2 } from '@/api/data-list'

@Component({
  components: {
    StudioHeader,
    Chart,
    GridLayout,
    GridItem,
    controlVue,
    pageTableVue,
    OperatorConfiguration,
    TextEditor,
    FilterFormToolbar,
    FilterForm,
  },
})
export default class PublishView extends Vue {
  public grids: any = []
  public layoutData: any = []
  public env: string = ''
  public resize: boolean = false
  public windowResizeChartSize: any = [100, 100]
  private configurationExecuteId: string = '' // 执行中的控件id，一次只允许一个控件执行
  private configurationExecuteSessionId: string = '' // 控件执行后的sessionId
  private projectId: string = ''
  private dashBoardId: string = ''
  private textPipelineId: number = 0
  private newPipelineId: number = 0
  private textRelationship: any = {}
  private isComplex: boolean = false
  private triggerLoading: any = null
  private hasTextChart: any = false

  //  for interact var
  private interactRelateList: Array<any> = [] //  [{child:..., parent:...}]
  private reverstInteractList: Array<any> = [] //  [{parent:..., children:...}]

  private connectedWidgetList: Array<Number> = []
  private parentList: Array<Number> = []
  private selectedParentList: Array<Number> = []
  private hideWidgetList: Array<Number> = []

  /** filter-form task running or not running status */
  private isFilterFormTaskRunning: boolean = false

  private filterFormExecuteId: string = ''

  public get containerStyle() {
    return {
      width: `${this.layoutData.width}px`,
      background: this.layoutData.background,
    }
  }

  public get unitX() {
    return this.layoutData.width / VisualizationStore.gridColNum
  }
  public get unitY() {
    return this.dataStore.gridRowHeight + this.dataStore.girdMargin[1]
    // return this.layoutData.height / VisualizationStore.gridRowHeight
  }

  public get windowWidth() {
    return document.documentElement.clientWidth
  }

  public get windowHeight() {
    return document.documentElement.clientHeight
  }

  @Watch('windowWidth', { immediate: true })
  getWindowWidth() {
    // console.log('windowWidth', this.windowWidth)
  }

  public async created() {
    VisualizationStore.changeVisualizationMode('publish')
  }

  @Watch('windowHeight', { immediate: true })
  getWindowHeight() {
    // console.log('windowHeight', this.windowHeight)
  }
  //  后面考虑 代码复用问题 - grid-layout-design
  public get formulaList() {
    return VisualizationStore.getFormulaList
  }

  @Watch('formulaList', { immediate: true, deep: true })
  handleFormulaListChange(newValue: any) {
    if (newValue) {
      //  check 是否失效，是否结果有误 'UPDATED_FAILED', 'DELETED'
      newValue.forEach((list: any) => {
        list.formulaList.forEach((formula: any) => {
          if (['UPDATED_FAILED', 'DELETED'].includes(formula.status)) {
            this.modifyFormulaInText(formula, 'delete')
          } else if (formula.status === 'UPDATED_SUCCESS') {
            //  根据状态。更新result值
            this.modifyFormulaInText(formula, 'update')
          }
        })
      })
    }
  }
  //  chart选中的维度信息集合
  private get selecetedChartInfo() {
    return VisualizationStore.selectedChartInfo
  }

  @Watch('selecetedChartInfo', { deep: true })
  private handleSelectedInfoChange(newValue: any) {
    // this.checkIfEnableInteract()
    if (newValue.length > 0) {
      this.checkIfEnableInteract()
    }
  }

  //  判断那些被联动的chart, 是否满足条件getData 渲染
  private checkIfEnableInteract() {
    const selectedWidgetLength = this.selectedParentList.length
    const latestSelectWidgetId = this.selecetedChartInfo[
      this.selecetedChartInfo.length - 1
    ].widgetId

    this.interactRelateList.forEach((item: any) => {
      if (checkArrayIncludes(this.selectedParentList, item.parents)) {
        // 包含关系
        const index: any = this.gridList.findIndex(
          (instance: any) => instance.widgetId === item.child
        )
        this.resetFiltersForItem(index, item.child)
        if (
          item.parents.length <= selectedWidgetLength &&
          item.child !== latestSelectWidgetId && //  点击自己，不会重复渲染；
          item.parents.includes(latestSelectWidgetId) //  点击无关的chart， 已渲染的不要重复渲染
        ) {
          // 达到渲染条件
          const gridItem = this.gridList[index]
          if (gridItem.chartType === 'table') {
            this.getTableData(gridItem, [])
          } else {
            this.getWidgetData(gridItem.chartOptions, gridItem, index) //  widgetJson - config - filters 需要处理
          }
        }
      }
    })
  }

  //  设置相应的filter 为gridItem 【不考虑多级filter，只考虑一级】
  private resetFiltersForItem(index: number, targetId: number) {
    //  selecetedChartInfo 新增在末尾
    const targetItem = this.interactRelateList.find(
      (item) => item.child === targetId
    )
    //  先置空，在对重新添加filter
    const target = this.gridList[index]
    //  先置空，在对重新添加filter
    if (target.chartType === 'table' && target.chartOptions) {
      //  table 的情况
      this.$set(this.gridList[index].chartOptions, 'filters', [])
    } else if (this.gridList[index].dataQuery.data.widgetJson.config) {
      //  非table 的情况
      this.$set(
        this.gridList[index].dataQuery.data.widgetJson.config,
        'filters',
        []
      )
    }

    const result: any = []
    this.selecetedChartInfo.forEach((item: any) => {
      if (targetItem.parents.includes(item.widgetId)) {
        // 只处理和自己相关的filter && 在移除filter 的时候，同样要移除 - child widget 要改变；
        result.push({
          col: item.xAttrStr,
          filterType: '=',
          values: [item.xAttrValue],
          desc: item.desc,
        })
      }
    })
    if (target.chartType === 'table' && target.chartOptions) {
      this.$set(this.gridList[index].chartOptions, 'filters', result)
    } else if (this.gridList[index].dataQuery.data.widgetJson.config) {
      this.$set(
        this.gridList[index].dataQuery.data.widgetJson.config,
        'filters',
        result
      )
    }
  }

  modifyFormulaInText(newValue: any, flag: string) {
    setTimeout(() => {
      const elements = document.querySelectorAll('.mceNonEditable')
      let formula: any
      let result: any
      // eslint-disable-next-line no-restricted-syntax
      for (const item of elements) {
        const id = item.getAttribute('data-id')
        if (newValue.id === Number(id)) {
          formula = item.getAttribute('data-formula')
          result = item.getAttribute('data-result')
          break
        }
      }

      this.getTinymce().editors.forEach((editor: any) => {
        let content = cloneDeep(editor.getContent())

        content = content.replaceAll(
          `<a class="mceNonEditable formula-style" data-id="${newValue.id}" data-result="${result}" data-formula="${formula}">${formula}</a>`,
          flag === 'delete'
            ? `<a class="mceNonEditable error-style" data-id="${newValue.id}">ERROR</a>`
            : `<a class="mceNonEditable formula-style" data-id="${newValue.id}" data-result="${newValue.result}" data-formula="${newValue.formula}">${newValue.formula}</a>`
        )
        content = content.replaceAll(
          `<a class="mceNonEditable result-style" data-id="${newValue.id}" data-result="${result}" data-formula="${formula}">${result}</a>`,
          flag === 'delete'
            ? `<a class="mceNonEditable error-style" data-id="${newValue.id}">ERROR</a>`
            : `<a class="mceNonEditable result-style" data-id="${newValue.id}" data-result="${newValue.result}" data-formula="${newValue.formula}">${newValue.result}</a>`
        )
        if (flag === 'update') {
          content = content.replaceAll(
            `<a class="mceNonEditable error-style" data-id="${newValue.id}">ERROR</a>`,
            `<a class="mceNonEditable result-style" data-id="${newValue.id}" data-result="${newValue.result}" data-formula="${newValue.formula}">${newValue.result}</a>`
          )
        }
        editor.setContent(content)
      })
    }, 500)
  }

  private getGlobal = (): any => window

  private getTinymce = () => {
    const global = this.getGlobal()

    return global && global.tinymce ? global.tinymce : null
  }

  public getChartOptions(item: any) {
    const localOptions = JSON.parse(JSON.stringify(item.chartOptions))
    localOptions.size = this.windowResizeChartSize
    return localOptions
  }

  private createWaterMark() {
    const areaName = '.content-box'
    setTimeout(() => {
      setWatermark('之江天枢可视分析平台', areaName, true)
    }, 500)
  }

  inited: boolean = false

  relationship: any = {}

  public mounted() {
    if (this.$route.query !== undefined && this.$route.query.env === 'vr') {
      this.layoutData = {
        width: document.body.clientWidth,
        height: document.body.clientHeight - 64,
      }
      this.env = 'vr'
    } else {
      this.layoutData = {
        width: VisualizationStore.layoutData.width,
        height: VisualizationStore.layoutData.height,
      }
    }
    this.initConfigurationWS()

    axios
      .request({
        // 根据发布的token获取数据
        url: `/dashboard/${this.$route.params.token}`,
      })
      .then(async (response) => {
        VisualizationStore.setLoadingStatus(false)
        if (response.data.code === 100 && response.data.result.id) {
          const layout = response.data.result.publishLayout
            ? JSON.parse(response.data.result.publishLayout)
            : {}
          if (layout.w && layout.h) {
            VisualizationStore.setBackgroundWidth(Number.parseFloat(layout.w))
            VisualizationStore.setBackgroundHeight(Number.parseFloat(layout.h))
            this.layoutData = {
              width: Number.parseFloat(layout.w),
              height: Number.parseFloat(layout.h),
              background: layout.background,
            }
          }
          if (Array.isArray(layout.coordinations)) {
            VisualizationStore.setCoordination(layout.coordinations)
          }
          const {
            isComplex,
            projectId,
            id,
            showWatermark,
          } = response.data.result
          this.isComplex = isComplex
          //  是否显示水印 - 根据后端返回的配置参数决定
          if (showWatermark) {
            this.createWaterMark()
          }
          let { mappingJson } = response.data.result
          try {
            mappingJson = JSON.parse(mappingJson)
          } catch {
            mappingJson = {}
          }
          this.newPipelineId = mappingJson?.newPipelineId
          if (mappingJson?.newPipelineId) {
            this.$socket.emit('queryStatus', {
              pipelineId: mappingJson.newPipelineId,
            })
          }
          this.triggerPipelineByToken()
          this.projectId = projectId
          this.dashBoardId = id
          let widgetList: any = []
          if (isComplex) {
            // 有复制pipeline 的情况
            widgetList = await this.queryNewPipelineWidgetList(
              mappingJson.newPipelineId
            )
          }
          this.queryProject(response.data.result.projectId)
          if (this.projectId && this.newPipelineId) {
            this.relationship = mappingJson?.relationship || {}
            await this.datasetList()
          }
          this.inited = true
          setTimeout(() => {
            if (
              layout.gridItems &&
              Array.isArray(layout.gridItems) &&
              layout.gridItems.length >= 0
            ) {
              const ids: Array<number> = []
              layout.gridItems.forEach(
                (gridItem: KeyValueStore, index: number) => {
                  gridItem.rank = index
                  const type = gridItem.chartType.split('|')
                  gridItem.chartType = type.length > 1 ? type[1] : type[0]
                  // 处理graph的情况
                  if (gridItem.config.length !== 0) {
                    const config = JSON.parse(gridItem.config)
                    gridItem.dataQuery = {
                      data: {
                        id: config.chartOptions.dataId,
                        type: config.chartOptions.dataType,
                        widgetJson: config.widgetJson,
                      },
                    }
                    gridItem.chartOptions = config.chartOptions
                    gridItem.chartOptions.interactions = [
                      { type: 'element-single-selected' },
                    ]
                    if (
                      config?.interactionJson?.relateWidgets &&
                      config?.interactionJson?.relateWidgets.length > 0
                    ) {
                      gridItem.interactionJson = config.interactionJson
                    }
                  }

                  if (
                    isComplex &&
                    mappingJson.relationship[gridItem.chartOptions.dataId]
                  ) {
                    // 存在pipeline复制的情况
                    gridItem.dataQuery.data.id =
                      mappingJson.relationship[gridItem.dataQuery.data.id]
                    gridItem.chartOptions.pipelineId = mappingJson.newPipelineId
                    gridItem.chartOptions.dataId =
                      mappingJson.relationship[gridItem.chartOptions.dataId]
                    gridItem.chartOptions.taskId =
                      mappingJson.relationship[gridItem.chartOptions.taskId]
                    if (gridItem.chartOptions.info) {
                      gridItem.chartOptions.info.childId =
                        mappingJson.relationship[
                          gridItem.chartOptions.info.childId
                        ]
                      gridItem.chartOptions.info.parentId =
                        mappingJson.relationship[
                          gridItem.chartOptions.info.parentId
                        ]
                    }

                    if (gridItem.chartType === 'configuration') {
                      // 控件组件要获取新的pipeline下面的参数配置的值
                      const target = widgetList
                        .filter(
                          (item: { tid: number; widgets: any[] }) =>
                            item.tid === gridItem.chartOptions.dataId
                        )[0]
                        ?.widgets.filter(
                          (item: KeyValueStore) => item.type === 'configuration'
                        )[0]
                      if (target && target.data.params) {
                        const newValue = target.data.params.reduce(
                          (accu: any, current: any) => {
                            accu[current.name] = current.value
                            return accu
                          },
                          {}
                        )
                        gridItem.chartOptions.params.forEach((p: any) => {
                          p.value = newValue[p.name]
                        })
                        gridItem.chartOptions.value = newValue
                      }
                      if (target && target.data.info) {
                        gridItem.chartOptions.info = target.data.info
                      }
                    }
                  }
                  if (gridItem.chartType === 'text' && isComplex) {
                    this.textPipelineId = mappingJson.newPipelineId
                    this.textRelationship = mappingJson.relationship
                  }
                  if (gridItem.widgetId) {
                    ids.push(gridItem.widgetId)
                  } else if (!gridItem.chartOptions) {
                    layout.gridItems[index].chartOptions = {
                      showChart: false,
                      value: [],
                      size: [
                        Math.floor(
                          this.unitX * this.dataStore.defaultGridWidth
                        ) - 20,
                        Math.floor(
                          this.unitY * this.dataStore.defaultGridHeight
                        ) + 50,
                      ],
                    }
                  }
                }
              )

              const gridItems = (layout.gridItems as Array<any>) || []
              if (
                layout.filters &&
                isArray(layout.filters) &&
                layout.filters.length > 0
              ) {
                ;(layout.filters as Array<KeyValueStore>).forEach(
                  (filterItem) => {
                    const fieldConfig: Array<KeyValueStore> = []
                    filterItem.fieldConfig.forEach(
                      (fieldInfo: KeyValueStore) => {
                        const column: ColumnInterface = {
                          tableId: filterItem.dataId,
                          label: fieldInfo.column,
                          value: fieldInfo.column,
                          type: fieldInfo.columnType,
                          isDate:
                            controlTypeEnum.timePicker ===
                            fieldInfo.controlType,
                          isNumber: false, // 暂时不支持数值型
                        }
                        fieldConfig.push({
                          ...fieldInfo,
                          column,
                          componentId:
                            (fieldInfo.associationChart as Array<string>)[0] ||
                            '',
                          value: getDefaultValueFromDashBoardData(
                            fieldInfo.controlRender,
                            fieldInfo
                          ), // ieldInfo.defaultValue
                        })
                      }
                    )
                    gridItems.push({
                      ...filterItem,
                      fieldConfig,
                    })
                  }
                )
              }
              if (
                layout.tables &&
                isArray(layout.tables) &&
                layout.tables.length > 0
              ) {
                if (isComplex) {
                  // 存在pipeline复制的情况
                  layout.tables.forEach((gridItem: any) => {
                    if (
                      mappingJson.relationship[gridItem.chartOptions.dataId]
                    ) {
                      gridItem.chartOptions.pipelineId =
                        mappingJson.newPipelineId
                      gridItem.chartOptions.dataId =
                        mappingJson.relationship[gridItem.chartOptions.dataId]
                    }
                  })
                }
                gridItems.push(...layout.tables)
              }
              // VisualizationStore.setGridList( this.sortLayoutItemsByRowCol(gridItems))
              VisualizationStore.setGridList(gridItems)
              this.$nextTick(() => {
                this.parseInteractInfo()
                this.initWidget()
              })
            }
            if (this.env === 'vr') {
              const layoutLocal = JSON.parse(JSON.stringify(layout.gridItems))
              layoutLocal.forEach((item: any, i: number) => {
                item.columns = layout.gridItems[i].columns
                item.moved = layout.gridItems[i].moved
                item.showChart = layout.gridItems[i].showChart
              })
              this.flexLayout(layoutLocal)
            } else {
              this.grids = layout.gridItems
            }
          }, 100)
        }
      })
      .catch(() => {
        VisualizationStore.setLoadingStatus(false)
      })

    if (this.env === 'vr') {
      window.addEventListener('resize', () => {
        this.resize = true
        this.layoutData = {
          width: document.body.clientWidth,
          height: document.body.clientHeight - 64,
        }
        this.flexLayout(this.grids)
      })
    }
  }

  //  判断当前chart 是否显示hover status
  private checkIfShowHoverStatus(id: Number) {
    if (this.selectedParentList.length > 0) {
      //  存在parent chart 被点击的情况
      const result: any = []
      this.selectedParentList.forEach((i: Number) => {
        const target = this.reverstInteractList.find((j: any) => {
          return j.parent === i
        })
        result.push(...(target?.children || []))
      })

      return result.includes(id)
    }

    return false
  }

  //  解析gridList 各gridItem 中联动信息
  private parseInteractInfo() {
    // forEach this.gridList item - interactionJson - relateWidgets
    const result: any = []
    const reverseResult: any = []
    this.gridList.forEach((item: any) => {
      if (
        item.interactionJson?.relateWidgets &&
        item.interactionJson?.relateWidgets.length > 0
      ) {
        const list = item.interactionJson?.relateWidgets
        list.forEach((widget: any) => {
          if (widget.status === 'connected') {
            //  for reverse direction
            const indexR = reverseResult.findIndex(
              (i: any) => i.parent === item.widgetId
            )
            if (indexR >= 0) {
              reverseResult[indexR].children =
                reverseResult[indexR].children || []
              reverseResult[indexR].children.push(widget.widgetId)
            } else {
              reverseResult.push({
                parent: item.widgetId,
                children: [widget.widgetId], // 后续需要push
              })
            }
            //  for right direction
            const index = result.findIndex(
              (i: any) => i.child === widget.widgetId
            )
            if (index >= 0) {
              result[index].parents.push(item.widgetId)
            } else {
              result.push({
                child: widget.widgetId,
                parents: [item.widgetId], // 后续需要push
              })
            }
          }
        })
      }
    })
    this.interactRelateList = result
    this.reverstInteractList = reverseResult

    //  get connected widget list; [1,2,3,4]
    this.interactRelateList.forEach((item: any) => {
      this.connectedWidgetList.push(item.child)
      this.parentList.push(...item.parents)
    })
    this.hideWidgetList = this.connectedWidgetList.slice()
    this.parentList = [...new Set([...this.parentList])] //  remove duplicated param
  }

  public chartInstanceMaps: KeyValueStore = {}

  public barChart = 'barChart' // ['barChart', 'barChart']

  public gridList: Array<any> = []

  public get getGridList(): Array<GridItemDataInterface> {
    return VisualizationStore.gridList
  }

  @Watch('getGridList', { deep: true })
  private handleGridList() {
    this.gridList = this.getGridList
  }

  public get dataStore() {
    return VisualizationStore
  }

  private async queryNewPipelineWidgetList(newPipelineId: string) {
    // 查询复制pipeline下的widget信息，主要获取Configuration的新配置
    const response: any = await widgetQueryByProject({
      data: {
        projectId: this.projectId,
        pipelineId: newPipelineId,
      },
    })
    if (response.data.code === 100) {
      return response.data.result[1].taskWidgets || []
    }
    return []
  }

  sortchange(item: GridItemDataInterface, index: number, sortData: any) {
    if (item.chartType === 'table' && item.chartOptions) {
      this.$set(item.chartOptions, 'sortCol', [
        { name: sortData.key, sort: sortData.sort },
      ])
      this.getTableData(item, [])
    }
  }

  /**
   * 由于地图组件特殊,value 不是数组
   */
  checkIsNoData(gridItem: gridItemFilterInterface) {
    if (this.checkIsGeoMap(gridItem)) {
      return false
    }
    if (isArray(gridItem.chartOptions.value)) {
      return gridItem.chartOptions.value.length === 0
    }
    return false
  }

  checkIsGeoMap(item: KeyValueStore) {
    return (
      item.chartType &&
      isArray(item.chartType) &&
      item.chartType[1] === GEOMAP_NAME
    )
  }

  initWidget() {
    this.gridList.forEach((gridItem: GridItemDataInterface, index: number) => {
      parseChartOptionToFunction(gridItem.chartOptions)
      if (
        (gridItem.widgetId ||
          (gridItem.chartType === 'table' &&
            gridItem.chartOptions.dataId &&
            isArray(gridItem.chartOptions.columns) &&
            gridItem.chartOptions.columns.length > 0)) &&
        gridItem.chartOptions &&
        !this.hideWidgetList.includes(gridItem.widgetId) //  如果存在联动关系，则先不显示
      ) {
        this.getWidgetData(
          gridItem.chartOptions as KeyValueStore,
          gridItem,
          index
        )
      }
    })
    this.$nextTick(() => {
      //  更新最新的结果 不需要每次都获取公式列表；当获取完text data 之后去获取，然后更新
      if (this.isComplex && this.hasTextChart) {
        VisualizationStore.fetchFormulaList({
          projectId: Number(this.projectId),
          pipelineId: this.textPipelineId,
          relationship: this.textRelationship,
        })
      }
    })
  }

  public async queryProject(projectId: string) {
    await ProjectStore.queryProjectById(projectId)
  }

  onChartInstanceInit(chartInstance: any, item: GridItemDataInterface) {
    this.chartInstanceMaps[item.i] = chartInstance
  }

  //  CHECK 该图表区域是否被选中过，若已经选中 则返回true, 反之则false；
  private checkIfChartSelected(item: any) {
    if (this.selecetedChartInfo.length === 0) {
      return false
    }
    const result = this.selecetedChartInfo.find((info: any) => {
      return item.widgetId === info.widgetId
    })
    if (result?.widgetId) {
      return (
        item.xAttrValue === result.xAttrValue &&
        item.xAttrStr === result.xAttrStr
      )
    }
    return false
  }

  public onChartEvent = debounce(
    (data: any, item: any) => this.chartEvent(data, item),
    300
  )

  public chartEvent(data: any, item: any /* ,index:number */) {
    const isGraph = this.checkIsGraph(item)
    switch (data.name) {
      case 'selectedChange':
      case 'graphNodeClick':
        this.currentFilterFormItemAsCharts = []
        this.currentFilterIndex = ''

        if (this.parentList.includes(item.widgetId)) {
          const result = {
            xAttrStr: !isGraph ? item.chartOptions.labelKey : data.value.key,
            xAttrValue: !isGraph
              ? data.event.data.data[item.chartOptions.labelKey]
              : data.value.value,
            // ...data.event.data.data,
            widgetId: item.widgetId,
            desc: !isGraph
              ? item.dataQuery.data.widgetJson.config.keys[0].desc
              : '',
          }
          if (isGraph && item.chartOptions.value.nodes.length > 0) {
            const targetValue = item.chartOptions.value.nodes[0].attrs.filter(
              (att: any) => att.key === data.value.key
            )[0].value
            result.desc = typeof targetValue === 'string' ? 'varchar' : 'int' // graph中的数据属性只会有字符串和数字两种类型
          }

          if (this.checkIfChartSelected(result)) {
            //  click the same area again - cancel select
            this.removeSelectedConfig(item.widgetId)
            this.addHideList(item.widgetId)
          } else {
            //  new select
            this.removeSelectedConfig(item.widgetId)
            this.addHideList(item.widgetId)
            //  同一个widget 只保留一个filter info
            VisualizationStore.addSelectedChartInfo(result)

            this.selectedParentList = [
              ...new Set([...this.selectedParentList, item.widgetId]),
            ]
          }
        }
        // console.error('chart event', data)
        break

      default:
        //  console.error('chart event', data)
        break
    }
  }

  //  将目标widgetId 下关联的图表加入到hideList 中
  private addHideList(widgetId: Number) {
    /*  只会对当前widget 下关联的chart 加入hideList, 目前暂不支持逐级传递，如a-b-c
      add back hideWidgetList    reverstInteractList  
    */
    const targetChildren = this.reverstInteractList.find((x: any) => {
      return x.parent === widgetId
    })
    this.hideWidgetList = [
      ...new Set([...this.hideWidgetList, ...(targetChildren?.children || [])]),
    ]
  }

  public flexLayout(layout: any) {
    // 计算布局后宽高有的gird数量
    let gridNumberS = 1
    let gridNumberL = 1
    const shortSide =
      this.layoutData.width < this.layoutData.height
        ? this.layoutData.width
        : this.layoutData.height
    const area = this.layoutData.width * this.layoutData.height
    const scale = 1
    while (true) {
      gridNumberL = Math.floor(
        area / shortSide / ((shortSide / gridNumberS) * scale)
      )
      if (gridNumberS * gridNumberL >= layout.length) {
        break
      }
      gridNumberS += 1
    }

    const gridNumberW =
      shortSide === this.layoutData.width ? gridNumberS : gridNumberL
    const gridNumberH = gridNumberS + gridNumberL - gridNumberW
    const gridWidth = Math.floor(
      (this.layoutData.width * scale) /
        gridNumberW /
        Math.floor(this.layoutData.width / 130)
    )
    const gridHeight = Math.floor(this.layoutData.height / gridNumberH / 20)

    // 设置新的x,w,y,h
    const sorted = this.sortLayoutItemsByRowCol(layout)
    let index
    for (index = 0; index < sorted.length; index += 1) {
      const item = sorted[index]
      item.x = Math.floor(index % gridNumberW) * gridWidth
      item.w = gridWidth
      item.y = Math.floor(index / gridNumberW) * gridHeight
      item.h = gridHeight
    }

    // 设置新的size 估算
    this.windowResizeChartSize = [
      Math.floor((this.layoutData.width * scale) / (gridNumberW + 1)),
      Math.floor(this.layoutData.height / gridNumberH),
    ]

    this.grids = [].concat(sorted)
    this.grids.sort((item1: any, item2: any) => {
      return item1.rank > item2.rank
    })
    this.layoutData.grids = [gridNumberW, gridNumberH]
  }

  public sortLayoutItemsByRowCol(layout: any): any {
    return [].concat(layout).sort((a: any, b: any) => {
      if (a.y > b.y || (a.y === b.y && a.x > b.x)) {
        return 1
      }
      return -1
    })
  }

  public getBoundaryValue(layout: any) {
    let boundaryLeft = Infinity
    let boundaryRight = 0
    let boundaryTop = Infinity
    let boundaryBottom = 0
    // for (let i = 0; i < layout.length; i++) {
    //   let l = layout[i];
    //   boundaryLeft = Math.min(boundaryLeft, l.x)
    //   boundaryBottom = Math.max(boundaryBottom, l.y + l.h);
    //   boundaryRight = Math.max(boundaryRight, l.x + l.w);
    //   boundaryTop = Math.min(boundaryTop, l.y)
    // }
    layout.forEach((l: any) => {
      boundaryLeft = Math.min(boundaryLeft, l.x)
      boundaryBottom = Math.max(boundaryBottom, l.y + l.h)
      boundaryRight = Math.max(boundaryRight, l.x + l.w)
      boundaryTop = Math.min(boundaryTop, l.y)
    })

    return {
      boundaryLeft,
      boundaryRight,
      boundaryTop,
      boundaryBottom,
    }
  }

  public collapseInnerExtraSpace(layout: any, boundaryValue: any) {
    let gap = 0
    const {
      boundaryLeft,
      boundaryRight,
      boundaryTop,
      boundaryBottom,
    } = boundaryValue
    for (let i = boundaryRight; i >= boundaryLeft; i -= 1) {
      // 从右向左检测边界内水平方向上的空白
      if (layout.some((item: any) => i >= item.x && i < item.x + item.w)) {
        // 点与某个元素在水平方向上重合
        if (gap > 0) {
          // 检测到空白区域
          const startPosition = i + gap // 空白间隔起始点
          // eslint-disable-next-line no-loop-func
          layout.forEach((l: any) => {
            if (l.x > startPosition) {
              // 空白间隔起始点右侧的元素向左移 gap 距离
              l.x -= gap
            }
          })
          gap = 0 // 重置空白区域
        }
      } else {
        gap += 1 // 计算空白区域的宽度
      }
    }

    gap = 0
    for (let i = boundaryBottom; i >= boundaryTop; i -= 1) {
      // 从底向上检测边界内垂直平方向上的空白
      if (layout.some((item: any) => i >= item.y && i < item.y + item.h)) {
        // 点与某个元素在垂直方向上重合
        if (gap > 0) {
          // 检测到空白区域
          const startPosition = i + gap // 空白间隔起始点
          // eslint-disable-next-line no-loop-func
          layout.forEach((l: any) => {
            if (l.y > startPosition) {
              // 空白间隔起始点下侧的元素向上移 gap 距离
              l.y -= gap
            }
          })
          gap = 0 // 重置空白区域
        }
      } else {
        console.log('gap++')
        gap += 1 // 计算空白区域的高度
      }
    }
    return layout
  }

  public collapseOuterExtraSpace(layout: any, boundaryValue: any) {
    const { boundaryTop, boundaryLeft } = boundaryValue
    if (boundaryTop === 0 && boundaryLeft === 0) {
      return
    }
    layout.forEach((item: any) => {
      item.y -= boundaryTop // 元素不到顶
      item.x -= boundaryLeft // 元素不到左侧边
    })
  }

  public collides(l1: KeyValueStore, l2: KeyValueStore): boolean {
    if (l1 === l2) {
      return false
    } // same element
    if (l1.x + l1.w <= l2.x) {
      return false
    } // l1 is left of l2
    if (l1.x >= l2.x + l2.w) {
      return false
    } // l1 is right of l2
    if (l1.y + l1.h <= l2.y) {
      return false
    } // l1 is above l2
    if (l1.y >= l2.y + l2.h) {
      return false
    } // l1 is below l2
    return true // boxes overlap
  }

  public flexHorizontalSize(layout: any, itemToFlex: any, boundaryValue: any) {
    let isCollision = false
    const { boundaryLeft, boundaryRight } = boundaryValue
    while (itemToFlex.x > boundaryLeft && !isCollision) {
      // detect position x change 计算元素左侧的空白区域
      itemToFlex.x -= 1
      itemToFlex.w += 1
      let index = 0
      for (; index < layout.length; index += 1) {
        if (
          this.collides(itemToFlex, layout[index]) &&
          layout[index].i !== itemToFlex.i
        ) {
          // 检测是否有碰撞
          break
        }
      }
      if (index !== layout.length) {
        // 有碰撞恢复变化前的值
        isCollision = true
        itemToFlex.x += 1
        itemToFlex.w -= 1
      }
    }

    isCollision = false
    while (itemToFlex.x + itemToFlex.w < boundaryRight && !isCollision) {
      // detect width change 计算元素右侧空白区域
      itemToFlex.w += 1
      let index = 0
      for (; index < layout.length; index += 1) {
        if (
          this.collides(itemToFlex, layout[index]) &&
          layout[index].i !== itemToFlex.i
        ) {
          // 检测是否有碰撞
          break
        }
      }
      if (index !== layout.length) {
        // 有碰撞则恢复变化前的值
        isCollision = true
        itemToFlex.w -= 1
      }
    }

    return itemToFlex
  }

  public flexVerticalSize(layout: any, itemToFlex: any, boundaryValue: any) {
    let isCollision = false
    const { boundaryTop, boundaryBottom } = boundaryValue
    while (itemToFlex.y > boundaryTop && !isCollision) {
      // detect position x change 计算元素左侧的空白区域
      itemToFlex.y -= 1
      itemToFlex.h += 1
      let index = 0
      for (; index < layout.length; index += 1) {
        if (
          this.collides(itemToFlex, layout[index]) &&
          layout[index].i !== itemToFlex.i
        ) {
          // 检测是否有碰撞
          break
        }
      }
      if (index !== layout.length) {
        // 有碰撞恢复变化前的值
        isCollision = true
        itemToFlex.y += 1
        itemToFlex.h -= 1
      }
    }

    isCollision = false
    while (itemToFlex.y + itemToFlex.h < boundaryBottom && !isCollision) {
      // detect height change 计算元素下方的空白区域
      itemToFlex.h += 1
      let index = 0
      for (; index < layout.length; index += 1) {
        if (
          this.collides(itemToFlex, layout[index]) &&
          layout[index].i !== itemToFlex.i
        ) {
          // 检测是否有碰撞
          break
        }
      }
      if (index !== layout.length) {
        // 有碰撞恢复变化前的值
        isCollision = true
        itemToFlex.h -= 1
      }
    }
    return itemToFlex
  }

  getTextData(gridItem: GridItemDataInterface) {
    if (gridItem && gridItem.chartOptions && gridItem.chartType === 'text') {
      const dataSend: KeyValueStore = {
        data: {
          id: gridItem.widgetId,
          type: gridItem.chartType, //  'text'
        },
      }
      this.$set(gridItem, 'isLoading', true)
      getWidgetData(dataSend)
        .then((response: any) => {
          if (response.data.code === 100) {
            this.$set(
              gridItem.chartOptions,
              'value',
              response.data.result.extraData.chartOptions.value
            )
            this.$set(gridItem, 'isLoading', false)
          }
        })
        .catch(() => {
          this.$set(gridItem, 'isLoading', false)
        })
    } else {
      console.error('call error: getTableData must be used by table')
    }
  }

  getTableData(
    gridItem: GridItemDataInterface,
    filterSend: any[],
    withFilterSend?: boolean
  ) {
    const withFilter =
      this.currentFilterFormItemAsCharts.includes(gridItem.widgetId) ||
      withFilterSend
    if (gridItem && gridItem.chartOptions && gridItem.chartType === 'table') {
      const filterFormJson = withFilter ? gridItem.filterFormJson || [] : []
      const dataSend: KeyValueStore = {
        data: {
          id: gridItem.chartOptions.dataId,
          type: gridItem.chartOptions.dataType,
          tableJson: {
            curPage: withFilterSend ? 1 : gridItem.chartOptions.curPage,
            name: '_record_id_',
            pageSize: gridItem.chartOptions.pageSize,
            sortCol: gridItem.chartOptions.sortCol,
            filters: withFilter
              ? mergeFilterFormFilters(
                  gridItem.chartOptions.filters || [],
                  filterFormJson
                )
              : gridItem.chartOptions.filters || [],
          },
        },
      }

      this.$set(gridItem, 'isLoading', true)
      getWidgetData(dataSend)
        .then((response: any) => {
          if (response.data.code === 1106) {
            this.errorFilterWidgets.push(gridItem.i)
            this.errorMsgMap[gridItem.i] = response.data.message
          }
          if ([100, 1106].includes(response.data.code)) {
            const gridItemClone = cloneDeep(gridItem)
            const { chartOptions } = gridItemClone
            const index = this.getGridIndex(gridItem.i as string)
            this.$set(this.gridList, index, {
              ...gridItemClone,
              isLoading: false,
              chartOptions: {
                ...chartOptions,
                curPage: response.data.result?.page?.curPage || 0,
                totalElements: response.data.result?.page?.totalElements || 0,
                pageSize: response.data.result?.page?.pageSize || 50,
                value: response.data.result
                  ? parseGetDataResult(response.data.result)
                  : [],
              },
            })

            //  remove target widget from hideList
            const targetIndex = this.hideWidgetList.indexOf(gridItem.widgetId)
            if (targetIndex >= 0) {
              this.hideWidgetList.splice(targetIndex, 1)
            }
          }
          if (withFilter) {
            setTimeout(() => {
              this.releaseLoadingTag(gridItem.widgetId)
            }, 100)
          }
        })
        .catch(() => {
          this.$set(gridItem, 'isLoading', false)
          if (withFilter) {
            setTimeout(() => {
              this.releaseLoadingTag(gridItem.widgetId)
            }, 100)
          }
        })
    } else {
      console.error('call error: getTableData must be used by table')
    }
  }

  onPageChange(item: KeyValueStore, index: number, pageInfo: KeyValueStore) {
    if (item.chartType === 'table' && item.chartOptions) {
      const filters: KeyValueStore | null = this.getFilterFormData(
        item.chartOptions.dataId,
        item.i as string
      )
      const filterSend = [] as Array<any>
      if (filters) {
        Object.keys(filters).forEach((fieldName: string) => {
          const controlRender = filters[fieldName].compType
          const value = filters[fieldName].value as any
          if (
            !['rangeDay', 'rangeYear', 'rengeMonth'].includes(controlRender) ||
            (['rangeDay', 'rangeYear', 'rengeMonth'].includes(controlRender) &&
              value[0] &&
              value[1])
          ) {
            const filterItem = this.getFilterItem(
              fieldName,
              value,
              controlRender,
              filters[fieldName].columnType
            )
            if (filterItem) {
              filterSend.push(filterItem)
            }
          }
        })
      }

      this.$set(item.chartOptions, 'pageSize', pageInfo.pageSize || 50)
      this.$set(item.chartOptions, 'curPage', pageInfo.pageNumber || 1)
      this.getTableData(item as GridItemDataInterface, filterSend)
    }
  }

  itemChange(
    gridItemFilter: gridItemFilterInterface,
    fieldInfo: KeyValueStore
  ) {
    const charts = (fieldInfo.associationChart as Array<string>) || []
    if (charts && charts.length > 0) {
      this.gridList.forEach((gridItem, index) => {
        if (charts.includes(gridItem.i as string)) {
          if (gridItem.chartType === 'table') {
            // 参数改变默认请求第一页
            this.$set(gridItem.chartOptions, 'curPage', 1)
          }
          this.getWidgetData(gridItem.chartOptions, gridItem, index)
        }
      })
    }
  }
  /**
   * 获取筛选参数
   */
  getFilterFormData(dataId: string, componentId: string): KeyValueStore | null {
    const gridFilters = this.gridList.filter((gridItem) => {
      return gridItem.chartType === 'filter' && gridItem.dataId === dataId
    })
    if (gridFilters.length > 0) {
      const result: KeyValueStore = {}
      gridFilters.forEach((item) => {
        item.fieldConfig.forEach((fieldInfo: ControlConfigurationInterface) => {
          if (fieldInfo.associationChart.includes(componentId)) {
            result[fieldInfo.column.value] = {
              compType: fieldInfo.controlRender,
              columnType: fieldInfo.columnType,
              value: fieldInfo.value,
            }
          }
        })
      })
      return result
    }
    return null
  }

  getFilterItem(
    fieldName: string,
    value: any,
    controlType: string,
    columnType?: string
  ): KeyValueStore | null {
    let result: any = null
    switch (controlType) {
      case componentTypeEnum.radio:
      case componentTypeEnum.select:
      case componentTypeEnum.remoteSelect:
        if (value && isString(value)) {
          result = {
            col: fieldName,
            filterType: '=',
            values: [],
            checkedList: [value],
          }
        }
        break

      case componentTypeEnum.checkbox:
        if (value && isArray(value) && value.length > 0) {
          result = {
            col: fieldName,
            type: columnType,
            filterType: 'in',
            values: [],
            checkedList: value,
          }
        }
        break

      case componentTypeEnum.rangeDay:
        if (isArray(value) && value.length === 2) {
          result = {
            col: fieldName,
            filterType: '[a,b]',
            type: 'timestamp',
            values: [value[0], value[1]],
            format: 'yyyy-MM-dd',
            checkedList: [value[0], value[1]],
          }
        }
        break
      case componentTypeEnum.rangeMonth:
        if (isArray(value) && value.length === 2) {
          result = {
            col: fieldName,
            filterType: '[a,b]',
            type: 'timestamp',
            values: [value[0], value[1]],
            format: 'yyyy-MM',
            checkedList: [value[0], value[1]],
          }
        }

        break
      case componentTypeEnum.rangeYear:
        if (isArray(value) && value.length === 2) {
          result = {
            col: fieldName,
            filterType: '[a,b]',
            type: 'timestamp',
            values: [value[0], value[1]],
            format: 'yyyy',
            checkedList: [value[0], value[1]],
          }
        }
        break
      default:
        break
    }

    return result
  }

  gridItemSyncBindData(parameterSet: any) {
    const { data, gridItem, index } = parameterSet
    const gridItemSet: GridItemDataInterface | null = bindChartData(
      gridItem,
      data
    )
    if (gridItemSet) {
      if (gridItemSet && gridItemSet.chartOptions) {
        const dom = (document as any).body.querySelector(
          `#preview-grid-item-${gridItemSet.i}`
        )
        if (dom) {
          gridItemSet.chartOptions.size = [dom.offsetWidth, dom.offsetHeight]
        }
      }
      setTimeout(() => {
        this.$set(gridItemSet, 'isLoading', false)
      }, 100)
      Vue.set(this.gridList, index, gridItemSet)
    }
  }

  //  remove selectedParentList;  remove selectedChartInfo
  private removeSelectedConfig(widgetId: Number) {
    const index = this.selecetedChartInfo.findIndex((i: any) => {
      return i.widgetId === widgetId
    })
    if (index >= 0) {
      //   VisualizationStore.removeSelectedChartInfo(index)
      VisualizationStore.setSelectedChartInfo(
        this.selecetedChartInfo.slice(0, index)
      )
      // 【直接根据数组位置，移除后面的所有】

      this.selectedParentList = this.selectedParentList.slice(0, index)
    }
  }

  /** 用于存储code为1106的response的widget */
  public errorFilterWidgets = [] as Array<any>
  /** 错误消息 */
  public errorMsgMap = {} as any

  getWidgetData(
    dataSetting: KeyValueStore,
    gridItem: GridItemDataInterface,
    index: number,
    withFilter?: boolean
  ) {
    if (checkIsFilterForm(gridItem)) {
      return
    }
    const filters: KeyValueStore | null = this.getFilterFormData(
      dataSetting.dataId,
      gridItem.i as string
    )
    const filterSend = [] as Array<any>
    if (filters) {
      Object.keys(filters).forEach((fieldName: string) => {
        const controlRender = filters[fieldName].compType
        const value = filters[fieldName].value as any
        if (
          !['rangeDay', 'rangeYear', 'rengeMonth'].includes(controlRender) ||
          (['rangeDay', 'rangeYear', 'rengeMonth'].includes(controlRender) &&
            value[0] &&
            value[1])
        ) {
          const filterItem = this.getFilterItem(
            fieldName,
            value,
            controlRender,
            filters[fieldName].columnType
          )
          if (filterItem) {
            filterSend.push(filterItem)
          }
        }
      })
    }
    if (gridItem.chartType === 'table') {
      this.getTableData(gridItem, filterSend, withFilter)
      return
    }
    if (gridItem.chartType === 'configuration') {
      return
    }
    if (gridItem.chartType === 'text') {
      this.hasTextChart = true
      this.getTextData(gridItem)
      return
    }

    const filterFormJson = gridItem.filterFormJson || []

    if (
      GRAPH_ARRAY.includes(
        Array.isArray(gridItem.chartType)
          ? gridItem.chartType[1]
          : gridItem.chartType
      )
    ) {
      this.getGraphData(gridItem, withFilter)
      return
    }
    const query = {
      data: gridItem.dataQuery?.data,
    }

    /** 筛选数据 */
    if (withFilter && query.data?.widgetJson?.config) {
      query.data.widgetJson.config.filters = mergeFilterFormFilters(
        query.data.widgetJson.config.filters || [],
        filterFormJson
      )
      //  remove selectedParentList;  remove selectedChartInfo
      this.removeSelectedConfig(gridItem.widgetId)
      this.addHideList(gridItem.widgetId)
    }

    if (enableConvertToCoor(gridItem.chartOptions)) {
      // 需要映射地理数据（省份、城市、ip等映射成经纬度）
      query.data.convert = gridItem.chartOptions.geoTransform.fields
    }
    // query.data.widgetJson.config.filters = filterSend
    this.$set(gridItem, 'isLoading', true)
    this.errorFilterWidgets =
      this.errorFilterWidgets?.filter((item) => item !== gridItem.i) || []
    getWidgetData(query)
      .then((response) => {
        if (response.data.code === 1106) {
          this.errorFilterWidgets.push(gridItem.i)
          this.errorMsgMap[gridItem.i] = response.data.message
        }
        if ([100, 1106].includes(response.data.code)) {
          const chartType = Array.isArray(gridItem.chartType)
            ? gridItem.chartType[1]
            : gridItem.chartType
          this.gridItemSyncBindData({
            data: {
              query,
              data: parseGetDataResult(
                response.data?.result || {
                  columns: [],
                  data: [],
                  extraData: {},
                },
                chartType,
                gridItem
              ),
              columns: gridItem.columns,
            },
            gridItem,
            index,
          })

          //  remove target widget from hideList
          const targetIndex = this.hideWidgetList.indexOf(gridItem.widgetId)
          if (targetIndex >= 0) {
            this.hideWidgetList.splice(targetIndex, 1)
          }
        }
        if (withFilter) {
          this.releaseLoadingTag(gridItem.widgetId)
        }
        this.$set(gridItem, 'isLoading', false)
      })
      .catch(() => {
        this.$set(gridItem, 'isLoading', false)
        if (withFilter) {
          this.releaseLoadingTag(gridItem.widgetId)
        }
      })
  }

  /** 获取tooltip渲染文字 */
  public getGraphTooltipInfo(chart: any, setting: any): Array<string> {
    console.log('chart', chart, 'setting', setting)
    if (setting.tooltipType === 1 && setting.nodeTooltipInfo) {
      return isArray(setting.nodeTooltipInfo.attrs) &&
        setting.nodeTooltipInfo.attrs.length > 0
        ? setting.nodeTooltipInfo.attrs.reduce(
            (rec: Array<string>, item: any) => {
              rec.push(`${item.key}：${item.value}`)
              return rec
            },
            [] as Array<string>
          )
        : ['-- 空 -- ']
    }
    if (setting.tooltipType === 2 && setting.linkTooltipInfo) {
      return isArray(setting.linkTooltipInfo.attrs) &&
        setting.linkTooltipInfo.attrs.length > 0
        ? setting.linkTooltipInfo.attrs.reduce(
            (rec: Array<string>, item: any) => {
              rec.push(`${item.key}：${item.value}`)
              return rec
            },
            [] as Array<string>
          )
        : ['-- 空 -- ']
    }
    return ['-- 空 --']
  }

  /**
   * 保存控件参数
   */
  private async saveConfigurationParams(gridItem: GridItemDataInterface) {
    if (this.configurationExecuteId) {
      return
    }
    const { pipelineId, dataId, params, value = {} } = gridItem.chartOptions
    const response: any = await simpleUpdate({
      // 更新算子节点的参数
      data: {
        ...gridItem.chartOptions.info,
        data: {
          algType: gridItem.chartOptions.info.algType,
          setParams: params.map((p: KeyValueStore) => {
            let newValue = p.value
            if (value[p.name] !== undefined) {
              newValue = p.type === 'int' ? +value[p.name] : value[p.name]
            }

            return {
              ...p,
              value: newValue,
            }
          }),
        },
        id: dataId,
        pipelineId,
        projectId: this.projectId,
      },
    })
    const parameters = buildSaveParameters(gridItem)
    if (this.dashBoardId) {
      parameters.data.dashboardId = this.dashBoardId
    }
    // if (gridItem.widgetId) {
    //   await updateWidget(parameters) // 保存configuration widget
    // }

    if (response.data.code === 100) {
      this.executeConfiguration(gridItem) // 执行
    } else {
      const { message, tips } = response.data
      this.$message.error(message || tips)
    }
  }

  /**
   * 停止执行
   */
  private async stopConfigurationExecute() {
    this.configurationExecuteId = ''
    const response = await pipelineStop({
      data: {
        projectId: this.projectId,
        sessionId: this.configurationExecuteSessionId,
      },
    })
    if (response.data.code === 100) {
      this.$message.success('停止成功')
    } else {
      this.$message.error(response.data.message || '停止失败，请稍候再试')
    }
  }

  /**
   * 执行控件
   */
  private async executeConfiguration(gridItem: GridItemDataInterface) {
    const { pipelineId, taskId } = gridItem.chartOptions
    this.configurationExecuteId = gridItem.widgetId
    const response: any = await pipelineExecute({
      data: {
        id: pipelineId,
        taskId,
        projectId: this.projectId,
        sampling: false,
      },
    })
    if (response.data.code === 100) {
      const [sessionId] = response.data.result
      this.configurationExecuteSessionId = sessionId
      this.$socket.emit('queryStatus', { pipelineId, sessionId })
    }
  }

  /**
   * 初始化监听控件的运行结果
   */
  private initConfigurationWS() {
    this.$socket.on('queryStatus', this.parseConfigurationWS)
  }

  /**
   * 解析控件执行结果
   */
  private parseConfigurationWS(data: any) {
    // const that = this
    console.log('parseConfigurationWS', data)
    // if (!this.configurationExecuteId) {
    //   return
    // }
    if (data.status === undefined) {
      if (data.sessionId && data.pipelineId) {
        this.$socket.emit('queryStatus', {
          pipelineId: data.pipelineId,
          sessionId: data.sessionId,
        })
      }
      return
    }
    const pipelineId = this.newPipelineId
    if (this.configurationExecuteId) {
      this.$message.info(
        data.status === 'SUCCESS'
          ? '控件执行成功，正在更新相关图表数据'
          : '控件执行失败，请检查配置稍候再试',
        4
      )
    }
    if (pipelineId) {
      //  update for formulaList result
      if (this.isComplex && this.hasTextChart) {
        VisualizationStore.fetchFormulaList({
          projectId: Number(this.projectId),
          pipelineId: this.textPipelineId,
          relationship: this.textRelationship,
        })
      }

      this.gridList.forEach((gridItem, index) => {
        if (
          gridItem.widgetId !== this.configurationExecuteId &&
          gridItem.chartOptions.pipelineId === pipelineId && // pipeline 下的其他chart获取数据
          gridItem.chartType !== 'configuration'
        ) {
          this.getWidgetData(
            gridItem.chartOptions as KeyValueStore,
            gridItem,
            index
          )
        }
      })
    }
    if (this.triggerLoading) {
      this.triggerLoading()
      this.triggerLoading = null
    }
    this.configurationExecuteId = ''
  }

  private async triggerPipelineByToken() {
    const response = await axios.request({
      // 根据发布的token去触发pipeline，由后端决定是否执行
      url: `/dashboard/trigger/${this.$route.params.token}`,
    })
    if (response.data.code === 100 && response.data.result === true) {
      this.triggerLoading = this.$message.info('项目第一次执行，请稍候', 0)
    }
  }

  /**
   * 获取graph 数据
   */
  private async getGraphData(
    gridItem: GridItemDataInterface,
    withFilter?: boolean
  ) {
    this.$set(gridItem, 'isLoading', true)
    const callback = withFilter
      ? (data: any) => filterGraphData(gridItem.filterFormJson || [], data)
      : undefined
    const value = await getWidgetGraphData(gridItem, this.projectId, callback)
    const { nodePosition = [] } = gridItem.chartOptions // 处理position位置
    if (gridItem.dataQuery?.data.widgetJson.config.filters.length > 0) {
      this.filterGraphData(
        value,
        gridItem.dataQuery?.data.widgetJson.config.filters
      )
    }
    value.nodes.forEach((node: any) => {
      const targetPosition = (nodePosition as Array<{
        id: string
        x: number
        y: number
      }>).filter((p) => p.id === node.id)[0]
      if (targetPosition) {
        node.x = targetPosition.x
        node.y = targetPosition.y
      }
    })

    this.$set(gridItem.chartOptions, 'value', value)
    this.$set(gridItem, 'showChart', true)
    this.$set(gridItem, 'isLoading', false)
    const targetIndex = this.hideWidgetList.indexOf(gridItem.widgetId)
    if (targetIndex >= 0) {
      this.hideWidgetList.splice(targetIndex, 1)
    }
    if (withFilter) {
      this.releaseLoadingTag(gridItem.widgetId)
    }
  }
  /**
   * 过滤graphData
   */
  private filterGraphData(
    value: any,
    filters: { col: string; values: any[] }[]
  ) {
    value.nodes = value.nodes.filter((node: any) => {
      // eslint-disable-next-line unicorn/prevent-abbreviations
      const parsedAttrs = node.attrs.reduce((accu: any, current: any) => {
        accu[current.key] = current.value
        return accu
      }, {})
      return filters.every((filter) =>
        filter.values.includes(parsedAttrs[filter.col])
      )
    })
  }

  beforeDestroy() {
    this.$socket.off('queryStatus', this.parseConfigurationWS)
    deleteWatermark('water_mark')
    removeResizeListener()
  }

  /** 重新请求数据 */
  reloadWidget(gridItem: GridItemDataInterface, index: number) {
    this.getWidgetData(gridItem.chartOptions, gridItem, index, true)
  }

  /** 获取gridIndex */
  getGridIndex(index: string) {
    return this.gridList.findIndex((item: any) => item.i === index)
  }

  hoverFilterFormInfo: HoverFilterFormInfo | null = null

  /** 筛选控件中的筛选条件改变 */
  filterChange(data: any, gridItem: GridItemDataInterface) {
    console.log('filterChange done', data, gridItem)
  }

  /** 获取关联图表 */
  get filterAssociationChart() {
    let result = [] as Array<string | number>
    if (this.hoverFilterFormInfo) {
      const currentGrid = this.gridList.find(
        (item) => item.i === this.hoverFilterFormInfo?.i
      )
      result = currentGrid?.chartOptions?.associationChart || []
    }
    return result
  }

  /** 当前高亮filterForm 相关信息 */
  currentFilterFormItemAsCharts: any[] = []

  currentFilterIndex: string = ''

  /** filter-form 点击 */
  filterFormClicked(item: GridItemDataInterface) {
    this.currentFilterFormItemAsCharts =
      item.chartOptions?.associationChart || []
    this.currentFilterIndex = item.i as string
  }

  /** 鼠标移入filter-form */
  handleFilterFormMouseEnter(gridItem: GridItemDataInterface) {
    this.hoverFilterFormInfo = {
      i: gridItem.i as any,
      widgetId: gridItem.widgetId,
    }
  }

  /** 鼠标移出fitler-form */
  handleFilterFormMouseLeave(gridItem: GridItemDataInterface) {
    if (this.hoverFilterFormInfo?.i === gridItem.i) {
      this.hoverFilterFormInfo = null
    }
  }

  /** 筛选器运行状态检测对象 */
  loadingMaps: any = {}

  /** 判断筛选执行情况 */
  checkIsRunning(gridItem: GridItemDataInterface) {
    return (
      this.loadingMaps[`widget_${gridItem.widgetId}`] &&
      this.loadingMaps[`widget_${gridItem.widgetId}`].length > 0
    )
  }

  /** 检测filter-form是否可执行或重置 */
  checkFilterFormIsEnabled(gridItem: GridItemDataInterface) {
    return checkFilterFormIsEnabled(gridItem)
  }

  /** 是否是筛选器控件 */
  checkIsFilterForm(gridItem: gridItemFilterInterface) {
    return checkIsFilterForm(gridItem)
  }

  /** 释放加载标识 */
  releaseLoadingTag(widgetId: string | number) {
    const key = `widget_${this.filterFormExecuteId}`
    this.$set(
      this.loadingMaps,
      key,
      this.loadingMaps[`widget_${this.filterFormExecuteId}`].filter(
        (id: any) => id !== widgetId
      )
    )

    setTimeout(() => {
      if (this.loadingMaps[key].length === 0) {
        this.isFilterFormTaskRunning = false
      }
    }, 100)
  }
  /** 加载关联图表数据 */
  loadAssociationCharts(gridItem: GridItemDataInterface) {
    const associationCharts = ((gridItem.chartOptions?.associationChart ||
      []) as Array<string | number>).filter((id) => {
      return this.gridList.findIndex((grid) => grid.widgetId === id) > -1
    })
    if (associationCharts.length === 0) {
      this.$message.info('筛选器没有有效关联组件')
      this.filterFormExecuteId = ''
      return
    }
    this.filterFormExecuteId = gridItem.widgetId
    this.isFilterFormTaskRunning = true
    this.$set(
      this.loadingMaps,
      `widget_${gridItem.widgetId}`,
      cloneDeep(associationCharts)
    )
    const filterFormWidgetJSON = getFilterDataFromFilterForm(gridItem)
    associationCharts.forEach((wegetId) => {
      const asGridItem = this.gridList.find((item) => {
        return item.widgetId === wegetId
      })
      if (asGridItem) {
        this.$set(asGridItem, 'filterFormJson', filterFormWidgetJSON)
        this.getWidgetData(
          asGridItem.chartOptions,
          asGridItem,
          this.getGridIndex(asGridItem.i),
          true
        )
      }
    })
  }

  /** 重置filter-form */
  filterFormReset(gridItem: GridItemDataInterface) {
    const index = this.getGridIndex(gridItem.i as any)
    let queryData = {} as any
    const { chartOptions } = gridItem
    const form = this.$refs[`filter-form-${gridItem.i}`] || []

    if (form && (form as any)[0]) {
      queryData = (form as any)[0].getResetData() || {}
    }
    const gridItemSet = {
      ...gridItem,
      chartOptions: {
        ...chartOptions,
        queryData,
      },
    }

    this.$set(this.gridList, index, gridItemSet)
    this.$nextTick(() => {
      this.loadAssociationCharts(gridItemSet)
    })
  }

  /** 执行或者停止筛选 */
  filterFormExecute(isStart: boolean, gridItem: GridItemDataInterface) {
    if (isStart) {
      this.loadAssociationCharts(gridItem)
    } else {
      this.filterFormExecuteId = ''
    }
  }

  checkIsGraph(item: KeyValueStore) {
    return GRAPH_ARRAY.includes(
      Array.isArray(item.chartType) ? item.chartType[1] : item.chartType
    )
  }

  dataset: any[] = []

  get pipelineId() {
    return this.newPipelineId
  }

  showFilterForm() {
    return this.inited && !this.pipelineId
      ? true
      : this.dataset && this.dataset.length > 0 && this.pipelineId
  }

  public async datasetList() {
    const postData = {
      projectId: this.projectId,
      pipelineId: this.newPipelineId,
    }
    const response = await queryDatalist2({ data: postData })
    if (response.data.code === 100) {
      this.dataset = (response.data.result || []) as any[]
    } else {
      this.$message.error('获取数据集失败')
    }
  }
}
</script>

<style lang="less" scoped>
.publish {
  background-color: #ebeaef;
  height: 100vh;
  width: 100%;

  .content-box {
    height: calc(100vh - 64px);
    overflow-y: scroll;
  }

  .grid-item {
    background-color: #fff;
    border: 1px solid #d9d9d980;
    border-radius: 4px;
    overflow: hidden;

    &.active,
    &:hover {
      border: 1px solid #c5c9ff;

      /deep/ .comp-filter-form-toolbar {
        opacity: 1;
        pointer-events: all;
      }
    }

    .grid-item-execute {
      background-color: rgb(255, 255, 255);
      cursor: pointer;
      font-size: 16px;
      padding: 2px;
      position: absolute;
      right: 6px;
      top: 5px;
      z-index: 999;
    }

    .progress-bar {
      animation: gradient 1.5s ease infinite;
      background: linear-gradient(
        90deg,
        #c0bfff78 25%,
        #5760e6 37%,
        #c0bfff78 63%
      );
      background-size: 400% 100%;
      height: 3px;
      position: absolute;
      top: 0;
      width: 100%;
      z-index: 999;
    }
  }

  .grid-item.hover-border {
    border: 1px solid rgb(197, 201, 255);
  }

  .grid-item.select-border,
  .grid-item.active-border {
    border: 1px solid rgb(105, 115, 255);
  }

  .text-editor {
    height: calc(100% - 60px);
    padding: 0 15px;
  }

  .height-reset {
    height: calc(100% - 18px);
  }

  .container {
    background-color: #fff;
    margin: 0 auto;
  }

  .loading-mask,
  .no-data {
    align-content: center;
    align-items: center;
    display: flex;
    height: 100%;
    justify-content: center;
    text-align: center;
    width: 100%;
    z-index: 100;

    .loading-status-img,
    .no-data-img {
      display: block;
      height: auto;
      max-width: 75%;
      width: 208px;
    }
  }
}

.interact-mask {
  background: #fff !important;
  height: calc(100% - 38px);
  margin-top: 38px;
  position: absolute;
  width: 100%;
  z-index: 100;

  &.no-title {
    height: 100%;
    margin-top: 0;
  }
}

.loading-mask {
  background: rgba(0, 0, 0, 0.05) !important;
  position: absolute;
  z-index: 1;

  .loading-status {
    align-content: center;
    align-items: center;
    color: #222432;
    display: flex;
    flex-flow: row nowrap;
    font-size: 12px;
    font-weight: 500;
    left: 50%;
    letter-spacing: 1px;
    line-height: 36px;
    position: absolute;
    top: 50%;
    transform: translate(-50%, -50%);
    vertical-align: middle;

    .loading-text {
      display: inline-block;
      padding-left: 4px;
    }

    .sync-icon {
      color: #5760e6;
      font-size: 24px;
    }
  }
}

.no-data {
  background: transparent;

  .no-data-block {
    align-items: center;
    display: flex;
    flex-flow: column;
    justify-content: center;
  }

  .no-data-text {
    color: #d9d9d9;
  }
}

.title-box {
  color: #5d637e;
  font-size: 14px;
  font-weight: 500;
  height: 38px;
  letter-spacing: 1px;
  line-height: 38px;
  overflow: hidden;
  padding: 0 16px;
  text-overflow: ellipsis;
  white-space: nowrap;
  width: 100%;

  /deep/ .comp-filter-form-toolbar {
    position: absolute;
    right: 16px;
    top: 0;
    z-index: 10;
  }

  &.no-title {
    display: none;
    height: 24px;
  }
}

/deep/ .chart-title {
  color: #5d637e !important;
  font-size: 14px !important;
  font-weight: 500;
  height: 38px;
  line-height: 38px;
  padding: 0 16px;
  text-align: left !important;
}

.control-box-list {
  padding-bottom: 0;
  padding-top: 14px;

  &.list-vertical {
    padding-bottom: 2px;
  }
}

.graph-tooltip {
  background: #fff;
  border-radius: 2;
  box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.15);
  max-width: 200px;
  min-width: 80px;
  padding: 12px;
  pointer-events: none;
  position: absolute;
  z-index: 100000;

  p {
    color: #555;
    font-size: 12px;
    line-height: 16px;
    margin: 0;
    overflow: hidden;
    padding: 0;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}
@keyframes gradient {
  0% {
    background-position: 100% 50%;
  }

  100% {
    background-position: 0% 50%;
  }
}

.filter-form-hander {
  background: transparent;
  box-sizing: border-box;
  height: 100%;
  left: 0;
  padding-top: 24px;
  position: absolute;
  top: 0;
  width: 100%;
  z-index: 8;
}

.publish .grid-item.remove-border {
  border: 0;
}

.remove-title {
  display: none;
}
</style>
